# -*- coding:utf-8 -*- #

from twisted.internet.protocol import Factory,Protocol
from twisted.internet import defer,threads
from twisted.internet import reactor
from twisted.protocols.basic import LineReceiver
from twisted.internet.error import CannotListenError

import time
#使工作线程>512
import os
#if os.name!='nt':
    #from twisted.internet import epollreactor
    #epollreactor.install()
#else:
    #from twisted.internet import iocpreactor
    #iocpreactor.install()

#引入环境变量，让python查找模块的时候到本目录查找
import sys
sys.path.append(os.path.abspath(os.path.dirname(__file__)))

#加载服务端配置
from config import CernterServerConfig
#加载数据分析模块
from core import data_analysis
#加载日志模块
from core.log import getLogger
logger = getLogger()
 
class CernterServer(LineReceiver):
    def __init__(self):
        self.recvBuf = ""
        self.clientinfo = {} # 信息格式 {'000c29b3972f': '192.168.168.100'}

        self.uid = None     #用来存储oid
        self.threads = []    #单用户的并发线程池
        
    #发送文本格式数据
    def sendData(self,callid,data):
        newdata = data_analysis.sendData(callid,data)
        self.transport.write(newdata)
    
    def sendError(self,callid,act,msg,disconn=False,pdata=None):
        errmsg = {}
        errmsg['succ']=0
        errmsg['act']=act
        errmsg['msg']= msg
        errmsg['data']= pdata
        self.sendData(callid,errmsg)
        #主动断开客户端连接
        if disconn:
            self.transport.loseConnection()
            

    #连接断开时候发生
    def connectionLost(self, reason):
        #客户端端口后调用 ＃主动踢掉client通过sendError进行
        #print("OutUser : %s"%(self.transport.getPeer()))
        self.removeFactoryClientips()
        Protocol.connectionLost(self, reason)
        
        if self.uid != None:
            self.factory.delClient(self)
        
    #新客户端进入
    def connectionMade(self):
        #连接客户端的ip地址
        client_ip = self.transport.getPeer().host
        
        logger.info("NewClient---> : %s"%(client_ip))
        
        if client_ip in CernterServerConfig['refuseIpList'] :
            #不允许连接的ip
            self.sendError(0,'login',client_ip + '---IP拒绝连接',True)

        elif client_ip in self.factory.clientips :
            #只允许单一实例连接
            self.sendError(0,'login',client_ip + '---IP已经连接',True)
        else:
            self.factory.clientips.append(client_ip)

    def removeFactoryClientips(self):
        client_ip = self.transport.getPeer().host
        logger.info("连接断开=====> %s " % client_ip)
        if client_ip in self.factory.clientips :
            self.factory.clientips.remove(client_ip)

    #接收数据
    def dataReceived(self, data):
        client_ip = self.transport.getPeer().host
        self.recvBuf += data
        cmddata = None
        while len(self.recvBuf):
            try:
                getdecode = data_analysis.decodedata(self.recvBuf,data)
                #logger.debug("getdecode--->%s" % getdecode)
                if getdecode is True:
                    break
                if getdecode is False:
                    self.recvBuf=""
                    break
                self.recvBuf = getdecode[0]
                callid = getdecode[1]
                cmddata = getdecode[2]
            except:
                logger.error("数据不能解压或不是json数据--->%s",client_ip)

            if cmddata is not None:
                self.InvokeAction(callid,cmddata,client_ip)

    #命令调度控制
    def InvokeAction(self,callid,cmddata,client_ip):

        if not cmddata.has_key('act'):
            self.sendError(callid,'undefined','没有发现act关键字',True)
            logger.error("没有发现act关键字--->%s",client_ip)
            return

        act = cmddata.pop('act')
        if act == 'regos':
            #注册主机，并写入数据库
            workRegos(cmddata)
            
            oid = cmddata['oid']
            self.uid = oid
            self.factory.addClient(self,cmddata)
  
        elif act == 'task':
            #执行任务，获取oid并发送到相应的主机
            self.factory.execTask(cmddata)
        elif act == 'end':
            #ep返回执行脚本结果
            #workEnd(cmddata)
            taskstatus = cmddata['code']
            if taskstatus == 0:
                taskmsg = cmddata['msg']
            else:
                taskmsg = cmddata['errmsg']
            commitLogs(1002,taskmsg)
            
        elif act == 'update':
            #ep升级操作
            self.factory.execUpdateTask(cmddata)
        elif act == 'aws':
            #aws操作，开机，关机,更新等
            oid = cmddata['oid']
            self.uid = oid
            self.factory.addClient(self,cmddata)
            
            self.factory.execAWS(cmddata)
        elif act == 'cron':
            #aws操作，开机，关机,更新等
            oid = cmddata['oid']
            self.uid = oid
            self.factory.addClient(self,cmddata)
            
            self.factory.execCron(cmddata)
        elif act == 'monitor':
            #aws操作，开机，关机,更新等
            oid = cmddata['oid']
            self.uid = oid
            self.factory.addClient(self,cmddata)
            
            self.factory.execMonitor(cmddata)
        elif act == 'project':
            
            oid = cmddata['oid']
            self.uid = oid
            self.factory.addClient(self,cmddata)
            
            self.factory.execProject(cmddata)
        elif act == 'operation':
            
            oid = cmddata['oid']
            self.uid = oid
            self.factory.addClient(self,cmddata)
            
            self.factory.execOperation(cmddata)
        else:
            logger.debug("Not Found act")
    
class CernterServerFactory(Factory):
    protocol = CernterServer
    #本系统最大允许10000人同时在线
    max_connections = 10000
    timeout = 3
    perdefer = 4 #每个进程运行的子线程数
    
    def __init__(self):
        self.clientips = [] # [ipaddr]   {ipaddr:nums}
        self.clients = {}   #{uid:<self>}
        self.servers = {} #存储到并行服务器的连接
        self.delayCallInstance = {}

    def addCallInstance(self,callid,calltype,instance,params):
        callid = str(callid)
        if not self.delayCallInstance.has_key(callid):
            self.delayCallInstance[callid] = [calltype,instance,params]
            
    def removeCallInstance(self,callid):
        callid = str(callid)
        ret = 0
        if self.delayCallInstance.has_key(callid):
            if  self.delayCallInstance[callid][1]!=None :
                if self.delayCallInstance[callid][0] == 'callLater' and self.delayCallInstance[callid][1].called==0:
                    self.delayCallInstance[callid][1].cancel()
                elif self.delayCallInstance[callid][0] == 'LoopingCall':
                    self.delayCallInstance[callid][1].stop()
                ret = callid
                self.delayCallInstance[callid][1]=None
            del self.delayCallInstance[callid]
        return ret

    def addClient(self,client,clientdata={}):
        if client.uid == None:
            client.sendError(0,'login','No UserId',True)
            return False
        else:
            oldclient = None
            uidkey = str(client.uid)
            if self.clients.has_key(uidkey):
                oldclient = self.clients[uidkey]
                oldclient.sendError(0,'Otherlogin','Other Client Logined',True)
                self.delClient(oldclient)

            self.clients[uidkey]=client
            
            if clientdata.has_key('conninfo'):
                ret = clientdata['conninfo']
                ret['starttime'] = time.time()
            else:
                ret = {'starttime':time.time()}
            return ret

    def delClient(self, client):
        if client.uid != None:
            uidkey = str(client.uid)
            if self.clients.has_key(uidkey) and self.clients[uidkey]==client:
                del self.clients[uidkey]

        client.transport.loseConnection()
        logger.debug('loseConnection:delClient===>%s' % client.uid)
        #修改数据库注册机器的状态
        workLostClient(client.uid)

    def execSend(self,data,client):
        """
        返回消息给客户端,对于单机的操作
        """
        client.sendData(1,data)
        
    def deferlistCallback(self,result,data,client):
        """
        拿到所有线程执行完返回的结果
        @param
        result [(True, {'rubberband': ' 2173'})]
        """
        
        result=[r[1] for r in result]
        
        data['success'] = 200
        data['msg'] = result
        
        client.sendData(2,data)
        
    def execUpdateTask(self,data):
        """
        执行ep升级任务
        """
        
        for oid,client in self.clients.iteritems():
            try:
                data['oid'] = oid
                client.sendData(100,data)
            except:
                print "[%s] lost connect" % oid
    
    #解析任务，并下发执行
    def execTask(self,data): 
        '''
        data数据说明
        {'oid': '000c2950b277,000c29cd9465,000c29b3972f', 
         'py': 'product.yw.rsync', 
         'rsync_server': '192.168.168.200', 
         'remote_dir': '/www/facebook', 
         'taskid': 60000651, 
         'model_name': 'flowershop'}
        '''
        logger.debug("recv task from web data ===>%s" % data)
        
        oid = data.pop('oid')
        
        #if oid.find(','):
        for oidinfo in oid.split(','):
            uidkey = str(oidinfo)
            try:
                client = self.clients[uidkey]
                data['oid'] = uidkey
                client.sendData(1,data)
            except KeyError:
                #如果没有对应的client，就执行任务失败操作
                taskmsg = "主机未连接到cm"
                workTaskLostClient(uidkey,data['taskid'],taskmsg)
                
                
    def execAWS(self,data):
        """
        执行aws的相关操作
        """

        py = data.pop('py')
        oid = data.pop('oid')
        uidkey = str(oid)
        client = self.clients[uidkey]
        data['oid'] = uidkey
        data['act'] = 'end'
        
        #更新实例信息
        if py == 'updateawsinstance':
            
            #d=threads.deferToThread(amazon.updateawsinstance,data)
            #d.addCallback(self.execSend,client)
            
            threads.deferToThread(amazon.updateawsinstance,data).addCallback(self.execSend,client)
            
        #销毁实例
        elif py == 'terminate':
            threads.deferToThread(amazon.terminate,data).addCallback(self.execSend,client)
        #关机
        elif py == 'stopped':
            threads.deferToThread(amazon.stopped,data).addCallback(self.execSend,client)
        #启动
        elif py == 'running':
            threads.deferToThread(amazon.running,data).addCallback(self.execSend,client)

        #重启
        elif py == 'reboot':
            threads.deferToThread(amazon.reboot,data).addCallback(self.execSend,client)

        #更新loadbalancer信息
        elif py == 'updateawslb':
            threads.deferToThread(amazon.updateawslb,data).addCallback(self.execSend,client)

        elif py == 'removeinstancefromcluster':
            threads.deferToThread(amazon.removeinstancefromcluster,data).addCallback(self.execSend,client)
            
        elif py == 'addinstancetocluster':
            threads.deferToThread(amazon.addinstancetocluster,data).addCallback(self.execSend,client)
            
        elif py == 'rsyncinstance':
            
            threads.deferToThread(amazon.rsyncinstance,data).addCallback(self.execSend,client)

        else:
            data['error'] = 500
            data['msg'] = "未发现操作方法"
        
        #client.sendData(1,data)

        #workTaskLostClient(uidkey,data['taskid'],taskmsg)
        
    def execCron(self,data):
        """
        执行计划任务的相关操作
        """
        
        logger.debug("recv cron from web data ===>%s" % data)

        py = data.pop('py')
        oid = data.pop('oid')
        uidkey = str(oid)
        client = self.clients[uidkey]
        data['oid'] = uidkey
        data['act'] = 'end'
        
        if py == 'cm':
            
            fp = open('cronconfig.py','w')
            fp.write(data['crondata'])
            fp.close()
        
            data['success'] = 200
            data['msg'] = "数据更新成功"

        else:
            data['error'] = 500
            data['msg'] = "未发现操作方法"
        
        client.sendData(1,data)
        
    def execMonitor(self,data):
        """
        执行监控的相关操作
        """
        
        logger.info("recv monitor from web data ===> %s" % data)
        
        webdata = {}
        weboid = data.pop('oid')
        webuidkey = str(weboid)
        webclient = self.clients[webuidkey]
        webdata['oid'] = webuidkey
        
        oidclient = data.pop('client')

        #将配置文件写入本地
        #open(CernterServerConfig['MONITOR_CFG'],'w+').writelines(data['filedata'])
        
        success_client = 0
        error_client = 0
        error_oid = ""
        for oidinfo in oidclient:
            uidkey = str(oidinfo)
            try:
                client = self.clients[uidkey]
                data['oid'] = uidkey
                client.sendData(1,data)
                success_client += 1
            except KeyError:
                error_client += 1
                error_oid = error_oid + uidkey
        
        webdata['success'] = 200
        if error_client == 0:
            webdata['msg'] = "监控下发成功:" + \
                          str(success_client) + \
                          "台,失败:0"
        else:
            webdata['msg'] = "监控下发成功:" + \
                          str(success_client) + \
                          "台,失败:"+ \
                          str(error_client) + \
                          "台,失败oid:" + error_oid

        
        webdata['act'] = 'end'
        webclient.sendData(1,webdata)        
      
    def execProject(self,data):
        """
        执行项目管理的相关操作
        """
        
        logger.debug("recv project from web data ===>%s" % data)

        py = data.pop('py')
        oid = data.pop('oid')
        uidkey = str(oid)
        client = self.clients[uidkey]
        data['oid'] = uidkey
        data['act'] = 'end'
        
        if py == 'updatersync':
            
            d=threads.deferToThread(project.updatersync,data)
            d.addCallback(self.execSend,client)
            
        elif py == 'updatesvnrepo':
            """
            提取仓库代码到本地
            """
            
            deferlist=[]
            svnrepodata = data.pop('svnrepodata')
            
            for k,v in svnrepodata.iteritems():
                d=threads.deferToThread(project.updatesvnrepo,k,v['username'],v['password'],v['localdir'],v['url'])
                deferlist.append(d)
                
            #创建deferredlist
            dl = defer.DeferredList(deferlist)
            #给deferredlist添加回调函数
            dl.addBoth(self.deferlistCallback,data,client)
            
            #msg = dosvn(data.pop('svnrepodata'))
            #data['success'] = 200
            #data['msg'] = msg
            #client.sendData(1,data)
        elif py == 'svncleanup':
            
            deferlist=[]
            svnrepodata = data.pop('svnrepodata')
            
            for k,v in svnrepodata.iteritems():
                d=threads.deferToThread(project.svncleanup,k,v['username'],v['password'],v['localdir'])
                deferlist.append(d)
                
            #创建deferredlist
            dl = defer.DeferredList(deferlist)
            #给deferredlist添加回调函数
            dl.addBoth(self.deferlistCallback,data,client)
            
            #msg = dosvncleanup(data.pop('svnrepodata'))
            #data['success'] = 200
            #data['msg'] = msg
            #client.sendData(1,data)
            
        elif py == 'svnupdatehook':
            """
            'svninfo': {'username': 'tangshuangfeng', 
                        'rsync_module': 'rubberband', 
                        'localdir': '/tmp/rubberband', 
                        'ip': ['10.208.40.97', '10.224.75.119'],
                        'distribute': 'yes',
                        'lb_name': 'test', 
                        'remotedir': '/data/www/rubberband', 
                        'password': '!@qw45FG*&'}
            """

            reactor.callInThread(dosvnhook,data.pop('svninfo'))
        else:
            data['error'] = 500
            data['msg'] = "未发现操作方法"
            client.sendData(1,data)
        
    def execOperation(self,data):
        """
        执行业务操作
        """

        py = data.pop('py')
        oid = data.pop('oid')
        uidkey = str(oid)
        client = self.clients[uidkey]
        data['oid'] = uidkey
        data['act'] = 'end'
        
        #更新实例信息
        if py == 'updateawsinstance':
            
            threads.deferToThread(amazon.updateawsinstance,data).addCallback(self.execSend,client)
            
        else:
            data['error'] = 500
            data['msg'] = "未发现操作方法"
        
        #client.sendData(1,data)

        #workTaskLostClient(uidkey,data['taskid'],taskmsg)
            
if __name__ == '__main__':

    #将打印都输出到文件中
    #if CernterServerConfig['CMLOG']!='' :
        #log.startLogging(DailyLogFile.fromFullPath(CernterServerConfig['CMLOG']))
        
    try:
        reactor.listenTCP(CernterServerConfig['SERVER_PORT'], CernterServerFactory())
        reactor.suggestThreadPoolSize(CernterServerConfig['suggestThreadPoolSize'])
        logger.info("\033[32;1mRunning Socket on %s:%s\033[0m" % ("", str(CernterServerConfig['SERVER_PORT'])))
    except CannotListenError:
        logger.info("\033[31;1mSocket %s  Address already in use.\033[0m" % str(CernterServerConfig['SERVER_PORT']))
        sys.exit()
    
#    d=threads.deferToThread(aSillyBlockingMethod,"2 secodns have passed")
#    d.addCallback(printResult)

    #执行计划任务
    #reactor.callInThread(watchfile,root_dir)
    
    reactor.run()
    